package com.ddxz.tool.core.convert;

import lombok.extern.slf4j.Slf4j;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author 朱宗刚
 * @version 1.0.0
 * @description com.casic.exchange.com.casic.common.tools.convert.DateUtil
 * @date 2020/2/28
 */

@Slf4j
public class DateUtil {
    public static final Long SECOND_PER_DAY = 86400L;
    public static final Long SECOND_PER_HOUR = 3600L;
    public static final Long SECOND_PER_MINUTE = 60L;
    public static final Long MILLISECOND_PER_DAY = 86400000L;
    public static final Long MILLISECOND_PER_HOUR = 3600000L;
    public static final Long MILLISECOND_PER_MINUTE = 60000L;
    public static final Long MILLISECOND_PER_SECOND = 1000L;

    /**
     * 获取当前时间戳
     * @return 精确到毫秒的时间戳
     */
    public static Long getTimestamp() {
        return System.currentTimeMillis();
    }

    /**
     * 获取当前时间戳
     * @return 精确到秒的时间戳
     */
    public static Long getCurrentTime() {
        return getTimestamp() / MILLISECOND_PER_SECOND;
    }

    /**
     * 将精确到秒时间戳转换为日期时间
     * @return 日期时间
     */
    public static Date toDate(long second) {
        return fromTimestamp(second * MILLISECOND_PER_SECOND);
    }

    /**
     * 将精确到毫秒时间戳转换为日期时间
     * @return 日期时间
     */
    public static Date fromTimestamp(long millisecond) {
        return new Date(millisecond);
    }

    /**
     * 将精确到毫秒的时间戳转换为指定格式的字符串
     * @param milliSecond 时间戳 毫秒
     * @param fmt 日期时间格式
     * @return 字符串表示的日期时间，如果转换失败则返回空字符串
     */
    public static String fmtDateTime(long milliSecond, String fmt) {
        try{
            SimpleDateFormat sdf = new SimpleDateFormat(fmt);
            return sdf.format(fromTimestamp(milliSecond));
        } catch (Exception e) {
            log.error("invalid date format or date out of range", e);
            return "";
        }
    }

    /**
     * 将字符串形式的日期时间，按照“yyyy-MM-dd HH:mm:ss.SSS”的方式转换为精确到毫秒的时间戳
     * @param datetime 字符串表示的日期时间
     * @return 精确到毫秒的时间戳
     */
    public static Long getMillisecondDateTime(String datetime) {
        return getMillisecond(datetime, CVT_DATETIME_FORMAT);
    }

    /**
     * 将字符串形式的日期时间，按照“HH:mm:ss.SSS”的方式转换为精确到毫秒的时间戳， 距离1970-01-01
     * @param time 字符串表示的日期时间
     * @return 精确到毫秒的时间戳
     */
    public static Long getMillisecondTime(String time, int flag) {
        return getMillisecond(toCvtDate(getTimestamp()) + " " + time, CVT_DATETIME_FORMAT);
    }

    /**
     * 将字符串形式的日期时间，按照“HH:mm:ss.SSS”的方式转换为精确到毫秒的时间戳，距离当天
     * @param time 字符串表示的日期时间
     * @return 精确到毫秒的时间戳
     */
    public static Long getMillisecondTime(String time) {
        return getMillisecond(time, CVT_TIME_FORMAT);
    }

    /**
     * 将字符串形式的日期时间，按照“yyyy-MM-dd”的方式转换为精确到毫秒的时间戳
     * @param date 字符串表示的日期时间
     * @return 精确到毫秒的时间戳
     */
    public static Long getMillisecondDate(String date) {
        return getMillisecond(date, STD_DATE_FORMAT);
    }

    /**
     * 将字符串形式的日期时间，按照“yyyy-MM-dd HH:mm:ss”的方式转换为精确到秒的时间戳
     * @param datetime 字符串表示的日期时间
     * @return 精确到秒的时间戳
     */
    public static Long getSecondDateTime(String datetime) {
        return getSecond(datetime, STD_DATETIME_FORMAT);
    }

    /**
     * 将字符串形式的日期时间，按照“yyyy-MM-dd”的方式转换为精确到秒的时间戳,距离1970-01-01
     * @param time 字符串表示的日期时间
     * @return 精确到秒的时间戳
     */
    public static Long getSecondTime(String time, int flag) {
        return getSecond(toCvtDate(getTimestamp()) + " " + time, STD_DATETIME_FORMAT);
    }

    /**
     * 将字符串形式的日期时间，按照“HH:mm:ss”的方式转换为精确到秒的时间戳 距离当天
     * @param time 字符串表示的日期时间
     * @return 精确到秒的时间戳
     */
    public static Long getSecondTime(String time) {
        return getSecond(time, STD_TIME_FORMAT);
    }

    /**
     * 将字符串形式的日期时间，按照“yyyy-MM-dd”的方式转换为精确到秒的时间戳
     * @param date 字符串表示的日期时间
     * @return 精确到秒的时间戳
     */
    public static Long getSecondDate(String date) {
        return getSecond(date, STD_DATE_FORMAT);
    }

    /**
     * 将字符串形式的日期时间，按照指定的方式转换为精确到秒的时间戳
     * @param date 字符串表示的日期时间
     * @param fmt 日期时间格式
     * @return 精确到秒的时间戳
     */
    public static Long getSecond(String date, String fmt) {
        Long result = getMillisecond(date, fmt);
        if(null == result) {
            return null;
        } else {
            return result / MILLISECOND_PER_SECOND;
        }
    }

    /**
     * 将字符串形式的日期时间，按照指定的方式转换为精确到毫秒的时间戳
     * @param date 字符串表示的日期时间
     * @param fmt 日期时间格式
     * @return 精确到毫秒的时间戳
     */
    public static Long getMillisecond(String date, String fmt) {
        try{
            SimpleDateFormat sdf = new SimpleDateFormat(fmt);
            return sdf.parse(date).getTime();
        } catch (Exception e) {
            log.error("invalid date format or date", e);
            return null;
        }
    }

    public static Date getDate(String date, String fmt) {
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(fmt);
            return simpleDateFormat.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
            return new Date(0L);
        }
    }

    public static Date getDate(String date) {
        return getDate(date, STD_DATE_FORMAT);
    }

    public static Date getStdTime(String time) {
        return getDate(time, STD_TIME_FORMAT);
    }

    public static Date getStdDateTime(String dt) {
        return getDate(dt, STD_DATETIME_FORMAT);
    }

    public static Date getCvtTime(String time) {
        return getDate(time, CVT_TIME_FORMAT);
    }

    public static Date getCvtDateTime(String dt) {
        return getDate(dt, CVT_DATETIME_FORMAT);
    }

    public static String toStrDate(Date date) {
        return fmtDateTime(null == date ? 0L : date.getTime(), STD_DATE_FORMAT);
    }

    public static String toStdTime(Date date) {
        return fmtDateTime(null == date ? 0L : date.getTime(), STD_TIME_FORMAT);
    }

    public static String toCvtTime(Date date) {
        return fmtDateTime(null == date ? 0L : date.getTime(), CVT_TIME_FORMAT);
    }

    public static String toStdDateTime(Date date) {
        return fmtDateTime(null == date ? 0L : date.getTime(), STD_DATETIME_FORMAT);
    }

    public static String toCvtDateTime(Date date) {
        return fmtDateTime(null == date ? 0L : date.getTime(), CVT_DATETIME_FORMAT);
    }

    /**
     * 将精确到毫秒的时间戳转换为指定格式的字符串
     * @param second 时间戳 秒
     * @param fmt 日期时间格式
     * @return 字符串表示的日期时间，如果转换失败则返回空字符串
     */
    public static String fmtDateTime(String fmt, long second) {
        return fmtDateTime(second * MILLISECOND_PER_SECOND, fmt);
    }

    /**
     * 将精确到秒的时间戳转换为标准日期格式
     * @param second 时间戳 秒
     * @return 年-月-日形式表示的日期
     */
    public static String toStdDate(long second) {
        return fmtDateTime(second * MILLISECOND_PER_SECOND, STD_DATE_FORMAT);
    }

    /**
     * 将精确到毫秒的时间戳转换为标准日期格式
     * @param milliSecond 时间戳 毫秒
     * @return 年-月-日形式表示的日期
     */
    public static String toCvtDate(long milliSecond) {
        return fmtDateTime(milliSecond, STD_DATE_FORMAT);
    }

    /**
     * 将精确到秒的时间戳转换为标准时间格式
     * @param second 时间戳 秒
     * @return 时:分:秒 形式表示的时间
     */
    public static String toStdTime(long second) {
        return fmtDateTime(second * MILLISECOND_PER_SECOND, STD_TIME_FORMAT);
    }

    /**
     * 将精确到毫秒的时间戳转换为标准时间格式
     * @param milliSecond 时间戳 毫秒
     * @return 时:分:秒.毫秒 形式表示的时间
     */
    public static String toCvtTime(long milliSecond) {
        return fmtDateTime(milliSecond, CVT_TIME_FORMAT);
    }

    /**
     * 将精确到秒的时间戳转换为标准的日期时间格式
     * @param second 时间戳 秒
     * @return 年-月-日 时:分:秒 形式表示的日期时间
     */
    public static String toStdDateTime(long second) {
        return fmtDateTime(second * MILLISECOND_PER_SECOND, STD_DATETIME_FORMAT);
    }

    /**
     * 将精确到毫秒的时间戳转换为标准的日期时间格式
     * @param milliSecond 时间戳 毫秒
     * @return 年-月-日 时:分:秒 形式表示的日期时间
     */
    public static String toCvtDateTime(long milliSecond) {
        return fmtDateTime(milliSecond, CVT_DATETIME_FORMAT);
    }

    /**
     * 计算时间差
     * @param begin 开始时间戳 秒
     * @param end 结束时间戳 秒
     * @return 以字符串表示的时间差
     */
    public static String intervalSecond(long begin, long end) {
        return secondInterval(end - begin);
    }

    /**
     * 计算时间差
     * @param begin 开始时间戳 毫秒
     * @param end 结束时间戳 毫秒
     * @return 以字符串表示的时间差
     */
    public static String intervalMilliSecond(long begin, long end) {
        return milliSecondInterval(end - begin);
    }

    /**
     * 计算指定时间戳与当前时间的时间差
     * @param second 开始时间戳 秒
     * @return 以字符串表示的时间差
     */
    public static String intervalSecond(long second) {
        return secondInterval(getCurrentTime() - second);
    }

    /**
     * 将时间差转换为字符串形式
     * @param second 时间差 秒
     * @return 字符串形式表示的时间差[[[*天]*:*:*]]
     */
    public static String secondInterval(long second) {
        if(second < 0) {
            return "";
        }

        long interval = second;
        StringBuilder builder = new StringBuilder();

        if(interval > SECOND_PER_DAY) {
            builder.append(interval / SECOND_PER_DAY).append("天");
            interval %= SECOND_PER_DAY;
        }

        builder.append(interval / SECOND_PER_HOUR).append(":");
        interval %= SECOND_PER_HOUR;
        builder.append(interval / SECOND_PER_MINUTE).append(":");
        interval %= SECOND_PER_MINUTE;
        builder.append(interval);
        return builder.toString();
    }

    /**
     * 计算指定时间戳与当前时间的时间差
     * @param milliSecond 开始时间戳 毫秒
     * @return 以字符串表示的时间差
     */
    public static String intervalMilliSecond(long milliSecond) {
        return milliSecondInterval(getTimestamp() - milliSecond);
    }

    /**
     * 将时间差转换为字符串形式
     * @param milliSecond 时间差 秒
     * @return 字符串形式表示的时间差[[[*天]*:*:*.*]]
     */
    public static String milliSecondInterval(long milliSecond) {
        if(milliSecond < 0) {
            return "";
        }

        long interval = milliSecond;
        StringBuilder builder = new StringBuilder();

        if(interval > MILLISECOND_PER_DAY) {
            builder.append(interval / MILLISECOND_PER_DAY).append("天");
            interval %= MILLISECOND_PER_DAY;
        }

        builder.append(interval / MILLISECOND_PER_HOUR).append(":");
        interval %= MILLISECOND_PER_HOUR;
        builder.append(interval / MILLISECOND_PER_MINUTE).append(":");
        interval %= MILLISECOND_PER_MINUTE;
        builder.append(interval / MILLISECOND_PER_SECOND).append(".");
        builder.append(interval % MILLISECOND_PER_SECOND);
        return builder.toString();
    }

    private static final String STD_DATE_FORMAT = "yyyy-MM-dd";
    private static final String STD_TIME_FORMAT = "HH:mm:ss";
    private static final String CVT_TIME_FORMAT = "HH:mm:ss.SSS";
    private static final String STD_DATETIME_FORMAT = STD_DATE_FORMAT + " " + STD_TIME_FORMAT;
    private static final String CVT_DATETIME_FORMAT = STD_DATE_FORMAT + " " + CVT_TIME_FORMAT;
}
